<?php

/*
 * Define Cache Control constants
 */
define('CACHE_PUBLIC', 1);
define('CACHE_PRIVATE', 2);
define('CACHE_NOCACHE', 4);
define('CACHE_NOSTORE', 8);
define('CACHE_REVALIDATE', 16);


/*
 * render_time_start()
 */
function render_time_start() {
	if (!isset($GLOBALS['render_start'])) {
		$GLOBALS['render_start'] = microtime(true);
	}
}


/*
 * render_time_end()
 */
function render_time_end() {
	return microtime(true) - $GLOBALS['render_start'];
}


/*
 * pretty_time()
 */
function pretty_time($secs, $html_entity = true) {
	$micro = ($html_entity === true ? " &#181;s" : " us");

	if ($secs >= 1) {
		return (string)round($secs, 3) . " s";
	} else if($secs >= 0.001) {
		return (string)round($secs * 1000, 3) . " ms";
	} else if($secs >= 0.000001) {
		return (string)round($secs * 1000000, 3) . $micro;
	} else {
		return (string)round($secs * 1000000000, 3) . " ns";
	}
}


/*
 * byte_units()
 */
function byte_units($base) {
	switch ($base) {
		case 0: return array(1, 'bytes', 'B');
		case 1: return array(1024, 'kilobytes', 'KB');
		case 2: return array(1048576, 'megabytes', 'MB');
		case 3: return array(1073741824, 'gigabytes', 'GB');
		case 4: return array(1099511627776, 'terabytes', 'TB');
		case 5: return array(1125899906842624, 'petabytes', 'PB');
	}

	return false;
}


/*
 * pretty_filesize()
 */
function pretty_filesize($path, $abbrev = true, $places = 2, $force_base = false, $trim = false) {
	$size = (is_numeric($path) ? $path : filesize($path));
	list($base, $unit, $unita) = byte_units($force_base === false ? $force_base : ($size > 0 ? floor(log($size, 1024)) : 0));
	$num = number_format($size / $base, ($base > 1 ? $places : 0));
	if ($trim) $num = preg_replace( '/\.([0-9]*?)0*$/', '.\1', $num );
	if (substr($num, -1) == '.') $num = substr($num, 0, -1);
	return $num . ' ' . ($abbrev == true ? $unita : $unit);
}


/*
 * selective_html_entity_decode()
 * takes only a single entity at a time
 */
function selective_html_entity_decode($entity) {
	$entity = $entity[0];
	switch (strtolower($entity)) {
		case '&lt;':
		case '&gt;':
		case '&amp;':
		case '&quot;':
		case '&apos;':
			break;
		case '&nbsp;':
			$entity = '&#xa0;';
			break;
		default:
			$entity = utf8_encode(html_entity_decode($entity));
			break;
	}
	return $entity;
}


/*
 * php_encode()
 * The equivalent of json_encode but to create a php array
 */
function php_encode(array $input, $format = false) {
	$values = array();
	foreach ($input as $k => $v) {
		if (is_string($k)) {
			$k = _php_encode_escape_string($k);
		}
		if (is_array($v)) {
			$v = php_encode($v, $format);
			if ($format) {
				$v = str_replace("\n", "\n\t", $v);
			}
		} else if (is_string($v)) {
			$v = _php_encode_escape_string($v);
		}
		$values[] = $k . ($format ? ' => ' : '=>') . $v;
	}
	$glue = $format ? ",\n\t" : ",";
	$values = implode($glue, $values);
	if ($format) {
		$values = "\n\t$values\n";
	}
	return "array($values)";
}


/*
 * _php_encode_escape_string()
 * Intended only as a helper function for php_encode, but it may be more useful than that.
 * If ever used elsewhere, change the damn name, cracka.
 */
function _php_encode_escape_string($s) {
	return '"'.addcslashes($s,"\\\"\r\n\t\0").'"';
}


/*
 * fileperms_str()
 */
function fileperms_str($path) {
	$perms = fileperms($path);

	if (($perms & 0xC000) == 0xC000) {
		$info = 's';	// Socket
	} else if (($perms & 0xA000) == 0xA000) {
		$info = 'l';    // Symbolic Link
	} else if (($perms & 0x8000) == 0x8000) {
		$info = '-';    // Regular
	} else if (($perms & 0x6000) == 0x6000) {
		$info = 'b';    // Block special
	} else if (($perms & 0x4000) == 0x4000) {
		$info = 'd';    // Directory
	} else if (($perms & 0x2000) == 0x2000) {
		$info = 'c';    // Character special
	} else if (($perms & 0x1000) == 0x1000) {
		$info = 'p';    // FIFO pipe
	} else {
		$info = 'u';    // Unknown
	}

	// Owner
	$info .= (($perms & 0x0100) ? 'r' : '-');
	$info .= (($perms & 0x0080) ? 'w' : '-');
	$info .= (($perms & 0x0040) ?
				(($perms & 0x0800) ? 's' : 'x') :
				(($perms & 0x0800) ? 'S' : '-'));

	// Group
	$info .= (($perms & 0x0020) ? 'r' : '-');
	$info .= (($perms & 0x0010) ? 'w' : '-');
	$info .= (($perms & 0x0008) ?
				(($perms & 0x0400) ? 's' : 'x') :
				(($perms & 0x0400) ? 'S' : '-'));

	// World
	$info .= (($perms & 0x0004) ? 'r' : '-');
	$info .= (($perms & 0x0002) ? 'w' : '-');
	$info .= (($perms & 0x0001) ?
				(($perms & 0x0200) ? 't' : 'x') :
				(($perms & 0x0200) ? 'T' : '-'));

	return $info;
}


/*
 * fileperms_oct()
 */
function fileperms_oct($path) {
	return substr(sprintf('%o', fileperms($path)), -4);
}


/*
 * emulate_404()
 */
function emulate_404($path = false) {
	if ($path == false) {
		$path = $_SERVER['PHP_SELF'];
	}

	ob_end_clean();
	no_cache();
	header('HTTP/1.1 404 Not Found');
	if (substr($_SERVER['SERVER_SOFTWARE' ], 0, 8) == 'Apache/2') {
?>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL <?=$path?> was not found on this server.</p>
<hr>
<?=$_SERVER['SERVER_SIGNATURE']?>
</body></html>
<?php
	}
	exit();
}


/*
 * no_cache()
 */
function no_cache($last_modified = false, $expires = false) {
	global $config;

	$config['meta']['Cache-Control'] = 'no-cache';
	header('Cache-Control: no-cache, must-revalidate');

	if ($last_modified !== false) {
		$str_lm = gmdate(DATE_RFC2822, $last_modified);
		$config['meta']['Last-Modified'] = $str_lm;
		header('Last-Modified: ' . $str_lm);
	}

	if ($expires !== false) {
		$str_ex = gmdate(DATE_RFC2822, $expires);
		$config['meta']['Expires'] = $str_ex;
		header('Expires: ' . $str_ex);
	}
}


/*
 * cache_control()
 */
function cache_control($directive = CACHE_NOCACHE, $last_modified = false, $expires = false) {
	global $config;

	if (CACHE_PUBLIC & $directive) {
		$directive_name = 'public';
	} else if (CACHE_PRIVATE & $directive) {
		$directive_name = 'private';
	} else if (CACHE_NOSTORE & $directive) {
		$directive_name = 'no-store';
	} else {
		// CACHE_NOCACHE || catchall
		$directive_name = 'no-cache';
		$config['meta']['Pragma'] = 'no-cache';
		header('Pragma: no-cache');
	}

	if (CACHE_REVALIDATE & $directive) {
		$directive_name .= ', must-revalidate';
	}

	$config['meta']['Cache-control'] = $directive_name;
	header("Cache-Control: " . $directive_name );

	if ($last_modified !== false) {
		$str_lm = is_string($last_modified) ? $last_modified : gmdate(DATE_RFC2822, $last_modified);
		$config['meta']['Last-Modified'] = $str_lm;
		header('Last-Modified: ' . $str_lm);
	}

	if ($expires !== false) {
		$str_ex = is_string($expires) ? $expires : gmdate(DATE_RFC2822, $expires);
		$config['meta']['Expires'] = $str_ex;
		header('Expires: ' . $str_ex);
	}
}


/*
 * if_modified_since()
 * accept an array of filenames and datetimes
 */
function if_modified_since(array $check) {
	$last_modified = 0;
	// foreach through array of filenames and datetimes
	foreach ($check as $timeorfile) {
		// if its a file, get filemtime on it and use that
		// if its a DBDateTime/Timestamp, convert to a timestamp and use it
		// if the timestamp is greater than $last_modified, replace $last_modified
	}
	// set the Last-Modified header

	if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
		$if_modified_since = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
		// if $last_modified == $if_modified_since
			// set header HTTP/1.1 304 Not Modified and exit
	}

}


/*
 * redirect()
 */
function redirect($url, $permanent = false) {
	header("HTTP/1.1 %s", $permanent ? "301 Moved Permanently" : "302 Found");
	header("Location: $url");

	exit();
}


/*
 * is_http_equiv()
 */
function is_http_equiv($name) {
	static $http_headers = array(
		'content-disposition', 'expires', 'pragma', 'content-type', 'content-script-type', 'content-style-type',
		'default-style', 'content-language', 'refresh', 'window-target', 'ext-cache', 'set-cookie',
		'pics-label', 'cache-control', 'vary', 'last-modified'
	);

	return in_array(strtolower($name), $http_headers);

	//$http_response_headers = array(
	//	'accept-ranges', 'age', 'allow', 'cache-control', 'content-disposition', 'content-encoding',
	//	'content-language', 'content-length', 'content-location', 'content-md5', 'content-range',
	//	'content-type', 'date', 'last-modified', 'location', 'server', 'set-cookie'
	//);
}


/*
 * last_modified()
 */
function last_modified($format = DATE_RFC2822) {
	$mod_time = filemtime($_SERVER['SCRIPT_FILENAME']);
	return date($format, $mod_time);
}


/*
 * pre_print_r()
 */
function pre_print_r($obj) {
	printf("<pre>%s</pre>", print_r($obj, true));
}



/*
 * syntax_check()
 */
function syntax_check($file) {
	//$path = pathinfo($file);

	//if (getcwd() != $path['dirname']) {
	//	$oldcwd = getcwd();
	//	@chdir($path['dirname']);
	//}

	//$result = @shell_exec('php -l ' . escapeshellarg($path['basename']));
	$result = @shell_exec('php -l ' . escapeshellarg($file));

	//if (isset($oldcwd)) {
	//	@chdir($oldcwd);
	//}

	return $result;
}


/*
 * logger()
 */
function logger($message, $log_level = LOG_INFO, $verb = null, $noun = null, $number = null) {
	static $minLevel = false;

	if ($minLevel === false) {
		$minLevel = is_defined('ERROR_REPORTING') ? constant('ERROR_REPORTING') : 3;
	}

	if ($log_level < $minLevel) {
		return false;
	}

	return DB::Table('Log')->NewRow(array(
		'Script'	=> $_SERVER['SCRIPT_FILENAME'],
		'IPAddress'	=> IP::ToInteger($_SERVER['REMOTE_ADDR']),
		'Verb'		=> $verb,
		'Noun'		=> $noun,
		'Level'		=> $log_level,
		'Number'	=> $number,
		'Message'	=> $message
	))->Save();
}


/*
 * ua()
 * used to test the useragent
 */
function ua($b, $eq = null, $v = null) {
	$ua = parse_ua();
	$b = strtolower($b);

	if (!isset($ua[$b])) {
		return false;
	}

	if (is_null($eq) && is_null($v)) {
		return $ua[$b];
	}

	$eq = strtoupper($eq);

	// $ua['mobile'] contains a string. comparisons are useless
	if ($b == 'mobile') {
		if ($eq == 'EQ' || $eq == '=') {
			return $ua[$b] == $v;
		} else {
			return false;
		}
	}

	if (!is_numeric($v)) {
		return false;
	}

	if (!is_float($v)) {
		$v = (float)$v;
	}

	$b = $ua[$b];

	switch ($eq) {
		case 'EQ':
		case '=':
			return $b == $v;
		case 'NEQ':
		case '!=':
			return $b != $v;
		case 'LT':
		case '<':
			return $b != 0 && $b < $v;
		case 'LTE':
		case '<=':
			return $b != 0 && $b <= $v;
		case 'GT':
		case '>':
			return $b > $v;
		case 'GTE':
		case '>=':
			return $b >= $v;
	}

	return false;
}


/*
 * parse_ua()
 * parses the useragent string sent in the HTTP request
 * translated from YAHOO.env.ua javascript code in the YAHOO global object of the YUI library
 */
function parse_ua() {
	if (isset($_SESSION['ua']) && $_SESSION['ua_string'] === $_SERVER['HTTP_USER_AGENT']) {
		return $_SESSION['ua'];
	}

	$ua = $_SESSION['ua_string'] = $_SERVER['HTTP_USER_AGENT'];

	$o = array(
		'ie'     => 0,
		'opera'  => 0,
		'gecko'  => 0,
		'webkit' => 0,
		'mobile' => null,
		'air'    => 0,
		'caja'   => 0
	);

	// Modern KHTML browsers should qualify as Safari X-Grade
	if (strpos($ua, 'KHTML') !== false) {
		$o['webkit'] = 1;
	}

	// Modern WebKit browsers are at least X-Grade
	if (preg_match('/AppleWebKit\/([^\s]*)/', $ua, $m)) {
		$o['webkit'] = (float)$m[1];

		// Mobile browser check
		if (strpos($ua, ' Mobile/') !== false) {
			$o['mobile'] = "Apple"; // iPhone or iPod Touch
		} else {
			if (preg_match('/NokiaN[^\/]*/', $ua, $m)) {
				$o['mobile'] = $m[0]; // Nokia N-series, ex: NokiaN95
			}
		}

		if (preg_match('/AdobeAIR\/([^\s]*)/', $ua, $m)) {
			$o['air'] = $m[0]; // Adobe AIR 1.0 or better
		}
	}

	if (!$o['webkit']) { // not webkit
		// @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
		if (preg_match('/Opera[\s\/]([^\s]*)/', $ua, $m)) {
			$o['opera'] = (float)$m[1];
			if (preg_match('/Opera Mini[^;]*/', $ua, $m)) {
				$o['mobile'] = $m[0]; // ex: Opera Mini/2.0.4509/1316
			}
		} else { // not opera or webkit
			if (preg_match('/MSIE\s([^;]*)/', $ua, $m)) {
				$o['ie'] = (float)$m[1];
			} else { // not opera, webkit, or ie
				if (preg_match('/Gecko\/([^\s]*)/', $ua, $m)) {
					$o['gecko'] = 1; // Gecko detected, look for revision
					if (preg_match('/rv:([^\s\)]*)/', $ua, $m)) {
						$o['gecko'] = (float)$m[1];
					}
				}
			}
		}
	}

	if (preg_match('/Caja\/([^\s]*)/', $ua, $m)) {
		$o['caja'] = (float)$m[1];
	}

	return $o;
}


function getConfig() {
	$cfg = $GLOBALS['config'];
	$args = func_get_args();
	foreach ($args as $arg) {
		if (isset($cfg[$arg])) {
			$cfg = $cfg[$arg];
		} else {
			return false;
		}
	}
	return $cfg;
}


/**
 * obey_literal()
 */
function obey_literal() {
	global $literals, $params;

	$param = $params[0];

	if (isset($literals[$param])) {
		$literal = $literals[$param];


		// $literal['include']
		if (isset($literal['include'])) {
			require_once($literal['include']);
		}


		// $literal['callback']
		if (isset($literal['callback'])) {
			$callback = $literal['callback'];

			if (is_callable($callback)) {
				if (call_user_func($callback, &$params, &$literal) === false) {
					return;
				}
			}
		}


		// $literal['disable']
		if (isset($literal['disable'])) {
			$disable = $literal['disable'];

			if ($disable === true) {
				return;
			} else if (is_callable($disable)) {
				if (call_user_func($disable, $params, $literal) === true) {
					return;
				}
			}
		}

		// $literal['redirect']
		if (isset($literal['redirect'])) {
			redirect($literal['redirect']);
		}


		// $literal['renderer']
		if (isset($literal['renderer'])) {
			$renderer = $literal['renderer'];

			if (is_callable($renderer)) {
				echo call_user_func($renderer, &$params);
				exit;
			}
		}


		// $literal['file'], etc
		$file = isset($literal['file']) ? $literal['file'] : $param;
		if (file_exists($file)) {

			$last_modified = filemtime($file);

			// if modified since
			if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
				$if_modified_since = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
				if ($last_modified == $if_modified_since) {
					header('HTTP/1.1 304 Not Modified');
					exit;
				}
			}

			// $literal['type']
			if (isset($literal['type'])) {
				header('Content-Type: ' . $literal['type']);
			}

			// $literal['file']
			if (isset($literal['name'])) {
				$name = $literal['name'];
				if ($name === true) {
					$name = basename($file);
				}
				if (is_string($name)) {
					$disposition = (isset($literal['attachment']) && $literal['attachment']) ? 'attachment' : 'inline';
					header('Content-Disposition: ' . $disposition . '; filename=' . $name);
				}
			}

			// gzip
			header('Vary: Accept-Encoding');
			if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false && file_exists($file . '.gz')) {
				header('Content-Encoding: gzip');
				$file .= '.gz';
			}

			header('Last-Modified: ' . gmdate(DATE_RFC1123, $last_modified));
			header('Content-Length: ' . filesize($file));

			/// eventually integrate these caching directives somehow
			//header('Expires: 0');
			//header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
			//header('Pragma: ');

			while (ob_get_level()) {
				ob_end_clean();
			}

			readfile($file);

		} else {
			//header('HTTP/1.1 404 Not Found');
			emulate_404($param);
		}

		exit;
	}
}



/*
 * IP
 */
class IP {

	/*
	 * IPToInteger()
	 */
	public static function ToInteger($ip) {
		$octets = explode('.', $ip);
		return ($octets[0] << 24) + ($octets[1] << 16) + ($octets[2] << 8) + $octets[3];
	}


	/*
	 * IntegerToIP()
	 */
	public static function FromInteger($ip) {
		$octets = array(
			($ip >> 24) % 256,
			($ip >> 16) % 256,
			($ip >> 8) % 256,
			$ip % 256
		);
		return implode('.', $octets);
	}
}

/*
 * URI
 */
class URI {

	var $hash;
	var $host;
	var $hostname;
	var $href;
	var $pathname;
	var $port;
	var $protocol;
	var $query;

	var $absolute = true;

	var $encodeEntities = false;

	var $uri;

	function __construct(array $parts = array()) {
		
	}

	static function parse($uri) {

	}

	function __toString() {

	}

}
